package com.hmdp.utils;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hmdp.dto.RedisData;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Slf4j
@Component
@RequiredArgsConstructor
public class CacheUtil {
    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper jsonMapper;

    public void set(String key, Object value, Long time, TimeUnit unit) {
        try {
            redisTemplate.opsForValue().set(key, jsonMapper.writeValueAsString(value), time, unit);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {
        try {
            RedisData redisData = new RedisData();
            redisData.setData(value);
            redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
            redisTemplate.opsForValue().set(key, jsonMapper.writeValueAsString(redisData));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
        String key = keyPrefix + id;
        String json = redisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)) {
            try {
                return jsonMapper.readValue(json, type);
            } catch (JsonProcessingException e) {
                log.error("数据不合法");
                return null;
            }
        }
        if (Objects.nonNull(json)) {
            return null;
        }
        R r = dbFallback.apply(id);
        if (Objects.isNull(r)) {
            redisTemplate.opsForValue().set(key, "", time, unit);
            return null;
        }
        this.set(key, r, time, unit);
        return r;
    }
}
